/**
* @brief	多功能环形缓冲区
* @file		XxxRingBuffer.c
* @author	何锡斌
* @email	2537274979@qq.com
* @date		2024/02/01
* @version	V1_0_0
* @par		实现功能：
* - 头插/尾插/头取/尾取/仅读不取/是否满/是否有数据/有多少数据/有多少空位/清空；
* - 灵活，满足先进先出(队列)、后进先出(栈)、数据窗口等多种场景需求；
* @par		注意事项：
* - 如果属于临界资源，请自行实现临界保护；
* - 实际有效的环形缓冲区最大长度为注册长度-1；
* - 直接使用插入函数造成环形缓冲区已满是允许数据覆盖的，如果不想出现这种情况，请利用`XxxRingBuffer_IsFull()`与`XxxRingBuffer_HowManyIdle()`结合插入函数自行二次封装；
* - 当消费速度大于生产速度(不会出现数据覆盖)，且尾插头取或头插尾取时，是支持同时插取的(支持一个任务插同时一个任务取)，即虽是临界资源但允许无需临界保护。注意同时插或同时取的任务仅限数量一(禁止多个任务同时插，或多个任务同时取，这时就需要临界保护)；
* @par		修改日志：
* <table>
* <tr><th>日期			<th>版本		<th>作者	<th>更新内容		</tr>
* <tr><td>2024/02/01	<td>V1_0_0		<td>何锡斌	<td>初版发布；		</tr>
* </table>
*/
#include "XxxRingBuffer.h"



#ifndef NULL
	#define NULL	((void*)0)
#endif /*NULL*/



/**环形缓冲区类*/
struct _STR_XxxRingBuffer{
	unsigned char* pBuf;			/**< 缓冲区起点指针 */
	unsigned char* pBufEnd;			/**< 缓冲区末尾指针 */
	unsigned char* pTail;			/**< 尾指针 */
	unsigned char* pHead;			/**< 头指针 */
};



/**
* @brief		获取环形缓冲区类的大小(用于申请内存)
* @param		null
* @return		环形缓冲区类的大小
* @par			注意事项：
* - null
* @par			示例:
* @code
*
* STR_XxxRingBuffer* m_pRBuf;
* m_pRBuf = (STR_XxxRingBuffer*)malloc(XxxRingBuffer_GetSize());
*
* @endcode
*/
unsigned char XxxRingBuffer_GetSize(void)
{
	return sizeof(STR_XxxRingBuffer);
}



/**
* @brief		注册
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[in]	pBuf				环形缓冲区的数据存储内存
* @param[in]	size				环形缓冲区大小(实际有效大小为该值-1)

* @return		注册是否成功
* - 0	注册成功
* - -1	pRingBuf为空指针，无效指针
* - -2	pBuf为空指针，无效指针
* - -3	size小于2，没意义
* @par			注意事项：
* - 由于实际有效大小会被削减一，所以size必须大于等于2；
* @par			示例:
* @code
*
* XxxRingBuffer_Register(m_pRBuf, m_buf, sizeof(m_buf));
*
* @endcode
*/
int XxxRingBuffer_Register(STR_XxxRingBuffer* pRingBuf, unsigned char* pBuf, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pBuf)return -2;								/* 返回失败：无效缓冲区指针 */
	if(size < 2)return -3;									/* 返回失败：缓冲区大小非法(必须大于等于2) */
	
	pRingBuf->pBuf = pBuf;
	pRingBuf->pBufEnd = pBuf +size -1;
	pRingBuf->pTail = pBuf;
	pRingBuf->pHead = pBuf;

	return 0;												/* 返回成功：注册成功 */
}



/**
* @brief		注销(由调用者自己回收内存)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return		注销是否成功
* - 0	注销成功
* - -1	pRingBuf为空指针，无效指针
* @par			注意事项：
* - null
*/
int XxxRingBuffer_Delete(STR_XxxRingBuffer* pRingBuf)
{
	if(NULL == pRingBuf)return -1;							/* 返回失败：环形缓冲区对象指针为空 */

	pRingBuf->pBuf = NULL;
	pRingBuf->pBufEnd = NULL;
	pRingBuf->pTail = NULL;
	pRingBuf->pHead = NULL;
	
	return 0;												/* 返回成功：注销成功 */
}



/**
* @brief		尾插数据
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[in]	pWriteBuf			准备写入的数据的指针
* @param[in]	size				想要插入的数据长度
* @return		被抛弃的数据长度
* - 0	没有数据被抛弃
* - >0	被抛弃的数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* @par			注意事项：
* - 数据覆盖(被抛弃)问题；
*/
int XxxRingBuffer_InsertTail(STR_XxxRingBuffer* pRingBuf, const unsigned char* pWriteBuf, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pWriteBuf)return -2;							/* 返回错误：无效数据指针 */
	
	int tempAbandonedNum = 0;								/* 被抛弃的数据长度(尾插抛弃的是"头部"数据) */

	while(size--)
	{
		/*先插入数据*/
		*(pRingBuf->pTail) = *pWriteBuf;					/* 尾插入数据 */
		++pWriteBuf;										/* 移向下一位数据 */

		/*再修改尾指针*/
		if(pRingBuf->pTail != pRingBuf->pBufEnd)			/* 尾指针不在末尾 */
		{
			++(pRingBuf->pTail);							/* 尾指针后移 */
		}
		else												/* 尾指针在末尾 */
		{
			pRingBuf->pTail = pRingBuf->pBuf;				/* 尾指针回到缓冲区开头 */
		}

		if(pRingBuf->pTail == pRingBuf->pHead)				/* 尾指针与头指针重合，说明写满缓冲区，抛弃位于"头部"的一个数据 */
		{
			/*修改头指针*/
			if(pRingBuf->pHead != pRingBuf->pBufEnd)		/* 头指针不在末尾 */
			{
				++(pRingBuf->pHead);						/* 头指针后移 */
			}
			else											/* 头指针在末尾 */
			{
				pRingBuf->pHead = pRingBuf->pBuf;			/* 头指针回到缓冲区开头 */
			}
			
			++tempAbandonedNum;								/* 抛弃总数 */
		}
	}

	return tempAbandonedNum;								/* 返回被抛弃的数据长度 */
}



/**
* @brief		头插数据
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[in]	pWriteBuf			准备写入的数据的指针
* @param[in]	size				想要插入的数据长度
* @return		被抛弃的数据长度
* - 0	没有数据被抛弃
* - >0	被抛弃的数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* @par			注意事项：
* - 数据覆盖(被抛弃)问题；
*/
int XxxRingBuffer_InsertHead(STR_XxxRingBuffer* pRingBuf, const unsigned char* pWriteBuf, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pWriteBuf)return -2;							/* 返回错误：无效数据指针 */

	int tempAbandonedNum = 0;								/* 被抛弃的数据长度(尾插抛弃的是"尾部"数据) */

	while(size--)
	{
		/*先修改头指针*/
		if(pRingBuf->pHead != pRingBuf->pBuf)				/* 头指针不在开头 */
		{
			--(pRingBuf->pHead);							/* 头指针前移 */
		}
		else												/* 头指针在开头 */
		{
			pRingBuf->pHead = pRingBuf->pBufEnd;			/* 头指针跳到缓冲区末尾 */
		}

		if(pRingBuf->pTail == pRingBuf->pHead)				/* 尾指针与头指针重合，说明写满缓冲区，抛弃位于"尾部"的一个数据 */
		{
			/*修改尾指针*/
			if(pRingBuf->pTail != pRingBuf->pBuf)			/* 尾指针不在开头 */
			{
				--(pRingBuf->pTail);						/* 尾指针前移 */
			}
			else											/* 尾指针在开头 */
			{
				pRingBuf->pTail = pRingBuf->pBufEnd;		/* 尾指针跳到缓冲区末尾 */
			}
			
			++tempAbandonedNum;								/* 抛弃总数 */
		}

		/*再插入数据*/
		*(pRingBuf->pHead) = *pWriteBuf;					/* 头插入数据 */
		++pWriteBuf;										/* 移向下一位数据 */
	}

	return tempAbandonedNum;								/* 返回被抛弃的数据长度 */
}



/**
* @brief		提取数据(从头部开始取出)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[out]	pReadBuf			保存读取数据的指针
* @param[in]	size				想要读取的数据长度
* @return		最终提取到的有效数据长度
* - 0	没有可提取的有效数据
* - >0	提取到的有效数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* @par			注意事项：
* - 
*/
int XxxRingBuffer_PickUpHead(STR_XxxRingBuffer* pRingBuf, unsigned char* pReadBuf, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pReadBuf)return -2;							/* 返回错误：无效读内存指针 */

	unsigned int tempValidDataNum;							/* 有效数据长度 */

	tempValidDataNum = XxxRingBuffer_HowManyData(pRingBuf);	/* 计算有多少有效数据 */
	if(size > tempValidDataNum)
	{
		size = tempValidDataNum;							/* 限制提取长度为有效长度 */
	}
	else
	{
		tempValidDataNum = size;							/* 保存提取长度 */
	}

	while(size--)
	{
		/*先读取数据*/
		*pReadBuf = *pRingBuf->pHead;
		++pReadBuf;

		/*再修改头指针*/
		if(pRingBuf->pHead != pRingBuf->pBufEnd)
		{
			++pRingBuf->pHead;
		}
		else
		{
			pRingBuf->pHead = pRingBuf->pBuf;
		}
	}

	return tempValidDataNum;								/* 返回成功：提取的有效数据长度 */
}



/**
* @brief		提取数据(从尾部开始取出)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[out]	pReadBuf			保存读取数据的指针
* @param[in]	size				想要读取的数据长度
* @return		最终提取到的有效数据长度
* - 0	没有可提取的有效数据
* - >0	提取到的有效数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* @par			注意事项：
* - 
*/
int XxxRingBuffer_PickUpTail(STR_XxxRingBuffer* pRingBuf, unsigned char* pReadBuf, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pReadBuf)return -2;							/* 返回错误：无效读内存指针 */

	unsigned int tempValidDataNum;							/* 有效数据长度 */

	tempValidDataNum = XxxRingBuffer_HowManyData(pRingBuf);	/* 计算有多少有效数据 */
	if(size > tempValidDataNum)
	{
		size = tempValidDataNum;							/* 限制提取长度为有效长度 */
	}
	else
	{
		tempValidDataNum = size;							/* 保存提取长度 */
	}

	while(size--)
	{
		/*先修改尾指针*/
		if(pRingBuf->pTail != pRingBuf->pBuf)
		{
			--pRingBuf->pTail;
		}
		else
		{
			pRingBuf->pTail = pRingBuf->pBufEnd;
		}
		
		/*再读取数据*/
		*pReadBuf = *pRingBuf->pTail;
		++pReadBuf;
	}

	return tempValidDataNum;								/* 返回成功：提取的有效数据长度 */
}



/**
* @brief		仅读数据(从头部开始读，只读不提取，读全部有效数据)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[out]	pReadBuf			保存读取数据的指针
* @return		最终读到的有效数据长度
* - 0	没有可读的有效数据
* - >0	读到的有效数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* @par			注意事项：
* - 
*/
int XxxRingBuffer_OnlyReadHead(STR_XxxRingBuffer* pRingBuf, unsigned char* pReadBuf)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pReadBuf)return -2;							/* 返回错误：无效读内存指针 */

	unsigned char* ptempRead;								/* 临时指针，用于保存指向环形缓冲区当前有效数据 */
	unsigned int tempValidDataNum;							/* 有效数据长度 */
	unsigned int tempNum;									/* 临时数据长度，用于遍历读取有效数据 */

	ptempRead = pRingBuf->pHead;							/* 指向头指针，准备读取 */
	tempValidDataNum = XxxRingBuffer_HowManyData(pRingBuf);	/* 计算有多少有效数据 */
	tempNum = tempValidDataNum;

	while(tempNum--)
	{
		*pReadBuf = *ptempRead;								/* 读取数据 */
		++pReadBuf;
		if(ptempRead != pRingBuf->pBufEnd)
		{
			++ptempRead;
		}
		else
		{
			ptempRead = pRingBuf->pBuf;
		}
	}

	return tempValidDataNum;								/* 返回成功：读取的有效数据长度 */
}



/**
* @brief		仅读数据(从头部开始读，只读不提取，读指定长度数据)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @param[out]	pReadBuf			保存读取数据的指针
* @param[in]	offset				从头部开始的偏移量
* @param[in]	size				想要读取的数据长度
* @return		最终读到的有效数据长度
* - 0	没有可读的有效数据
* - >0	读到的有效数据长度
* - -1	pRingBuf为空指针，无效指针
* - -2	pWriteBuf为空指针，无效指针
* - -3	offset为非法偏移量，偏移量大于环形缓冲区最大有效长度
* @par			注意事项：
* - 
*/
int XxxRingBuffer_OnlyReadHead_Len(STR_XxxRingBuffer* pRingBuf, unsigned char* pReadBuf, unsigned int offset, unsigned int size)
{
	if(NULL == pRingBuf)return -1;							/* 返回错误：无效对象指针 */
	if(NULL == pReadBuf)return -2;							/* 返回错误：无效读内存指针 */

	unsigned char* ptempRead;								/* 临时指针，用于保存指向环形缓冲区当前有效数据 */
	unsigned int tempValidDataNum;							/* 有效数据长度 */

	if(offset > (pRingBuf->pBufEnd -pRingBuf->pBuf))		/* 偏移量大于环形缓冲区最大有效长度 */
	{
		return -3;											/* 返回错误：非法偏移量 */
	}
	ptempRead = pRingBuf->pHead +offset;					/* 指向头指针+偏移量，准备读取 */
	if(ptempRead > pRingBuf->pBufEnd)						/* 超出环形缓冲区末尾 */
	{
		ptempRead = pRingBuf->pBuf +(ptempRead -pRingBuf->pBufEnd) -1;/* 从缓冲区起点+超出了多少 */
	}

	tempValidDataNum = XxxRingBuffer_HowManyData(pRingBuf);	/* 计算有多少有效数据 */
	tempValidDataNum -= offset;								/* 减去偏移量才是最终可读的有效数据长度 */
	if(size > tempValidDataNum)
	{
		size = tempValidDataNum;							/* 限制提取长度为有效长度 */
	}
	else
	{
		tempValidDataNum = size;
	}

	while(size--)
	{
		*pReadBuf = *ptempRead;								/* 读取数据 */
		++pReadBuf;
		if(ptempRead != pRingBuf->pBufEnd)
		{
			++ptempRead;
		}
		else
		{
			ptempRead = pRingBuf->pBuf;
		}
	}

	return tempValidDataNum;								/* 返回成功：提取的有效数据长度 */
}



/**
* @brief		是否有有效数据
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return
* - 0			没有有效数据
* - 1			拥有有效数据
* @par			注意事项：
* - null
*/
unsigned char XxxRingBuffer_IsHaveValidData(STR_XxxRingBuffer* pRingBuf)
{
	if(pRingBuf->pTail == pRingBuf->pHead)
	{
		return 0;											/* 返回失败：没有有效数据 */
	}
	return 1;												/* 返回成功：拥有有效数据 */
}



/**
* @brief		是否已满
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return
* - 0			未满
* - 1			已满
* @par			注意事项：
* - null
*/
unsigned char XxxRingBuffer_IsFull(STR_XxxRingBuffer* pRingBuf)
{
	unsigned int* ptempTail = pRingBuf->pTail;								/* 保存现场 */
	unsigned int* ptempHead = pRingBuf->pHead;								/* 保存现场 */

	/*已满的情况一：尾指针在头指针前一位*/
	if((ptempTail +1) == ptempHead)
	{
		return 1;															/* 返回成功：已满 */
	}
	/*已满的情况二：尾指针在缓冲区末尾，头指针在缓冲区开头*/
	if((ptempTail == pRingBuf->pBufEnd) && (ptempHead == pRingBuf->pBuf))
	{
		return 1;															/* 返回成功：已满 */
	}

	return 0;																/* 返回成功：未满 */
}



/**
* @brief		有多少有效数据
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return
* - 0			没有有效数据，但最好不要用来判断为空，可能是无效对象返回的零
* - >0			有效数据长度
* @par			注意事项：
* - null
*/
unsigned int XxxRingBuffer_HowManyData(STR_XxxRingBuffer* pRingBuf)
{
	if(NULL == pRingBuf)return 0;															/* 无效对象则返回零 */

	unsigned int* ptempTail = pRingBuf->pTail;												/* 保存现场 */
	unsigned int* ptempHead = pRingBuf->pHead;												/* 保存现场 */
	
	if(ptempTail >= ptempHead)																/* ▆▆▆□□□□ 或 □□▆▆▆□□ 或 □□□□□□□ */
	{
		return (ptempTail - ptempHead);
	}
	else																					/* ▆▆□□□▆▆ 或 □□□▆▆▆▆ */
	{
		return ((pRingBuf->pBufEnd -pRingBuf->pBuf +1) -(ptempHead -ptempTail));			/* 方法一：总长度-头尾指针间的空长度 */
		//return ((ptempTail -pRingBuf->pBuf) +(pRingBuf->pBufEnd -ptempHead +1));			/* 方法二：左边+右边  */
	}
}



/**
* @brief		有多少有效空闲位置
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return
* - 0			没有有效空闲位置，但最好不要用来判断已满，可能是无效对象返回的零
* - >0			有效空闲位置
* @par			注意事项：
* - null
*/
unsigned int XxxRingBuffer_HowManyIdle(STR_XxxRingBuffer* pRingBuf)
{
	if(NULL == pRingBuf)return 0;														/* 无效对象则返回零 */

	unsigned int* ptempHead = pRingBuf->pHead;											/* 保存现场 */
	unsigned int* ptempTail = pRingBuf->pTail;											/* 保存现场 */

	if(ptempHead > ptempTail)															/* ▆▆□□□▆▆ 或 □□□▆▆▆▆ */			
	{
		return (ptempHead - ptempTail -1);
	}
	else																				/* ▆▆▆□□□□ 或 □□▆▆▆□□ 或 □□□□□□□ */
	{
		//return ((pRingBuf->pBufEnd -pRingBuf->pBuf +1) -(ptempTail -ptempHead) -1);	/* 方法一：总长度-头尾指针间的已用长度-1 */
		return ((pRingBuf->pBufEnd -pRingBuf->pBuf) -(ptempTail -ptempHead));			/* 方法一优化 */
		//return ((ptempHead -pRingBuf->pBuf) +(pRingBuf->pBufEnd -ptempTail +1) -1);	/* 方法二：左边+右边-1  */
	}
}



/**
* @brief		清空(复位)环形缓冲区(软件层面上的清空，实际数据内容没有清零)
* @param[in]	pRingBuf			环形缓冲区对象指针
* @return
* - null
* @par			注意事项：
* - null
*/
void XxxRingBuffer_Empty(STR_XxxRingBuffer* pRingBuf)
{
	if(NULL == pRingBuf)return;

	pRingBuf->pTail = pRingBuf->pHead;
}
